import asyncio
import json
import os
from functools import wraps
from typing import List, Dict, Any, Optional

from dotenv import load_dotenv
from langchain_core.tools import tool
# Selenium 相关导入
from selenium import webdriver
from selenium.common.exceptions import (
    TimeoutException,
    NoSuchElementException,
    ElementNotInteractableException,
    WebDriverException
)
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager

from config import settings
from config.logging_config import get_logger
from llm import LLMFactory
# 导入通用工具管理器
from tools.ToolManager import ToolManager, SingleToolConfig
from tools.service.WebSearch import _web_search_tool
# 导入异步资源清理工具
from utils.async_cleanup import setup_cleanup_on_exit

# 配置日志

logger = get_logger(__name__)

# 加载环境变量
load_dotenv()

# 启用异步资源清理
setup_cleanup_on_exit()

# 全局浏览器实例
_browser_manager = None


# 默认浏览器配置
class DefaultBrowserConfig:
    def __init__(self):
        self.max_content_length = 2000


# 创建默认配置实例
config = DefaultBrowserConfig()


class BrowserManager:
    """浏览器管理器类，负责初始化和管理Chrome浏览器实例"""

    def __init__(self):
        """初始化浏览器管理器"""
        self.driver = None
        self.init_browser()

    def init_browser(self):
        """初始化Chrome浏览器"""
        try:
            # 配置Chrome选项
            chrome_options = Options()
            # 添加常用选项
            chrome_options.add_argument("--start-maximized")  # 最大化窗口
            chrome_options.add_argument("--disable-infobars")  # 禁用信息栏
            chrome_options.add_argument("--disable-extensions")  # 禁用扩展
            chrome_options.add_argument("--disable-gpu")  # 禁用GPU加速
            chrome_options.add_argument("--disable-dev-shm-usage")  # 禁用/dev/shm使用
            chrome_options.add_argument("--no-sandbox")  # 禁用沙盒

            # 添加资源清理相关选项
            chrome_options.add_argument("--disable-background-timer-throttling")
            chrome_options.add_argument("--disable-backgrounding-occluded-windows")
            chrome_options.add_argument("--disable-renderer-backgrounding")
            chrome_options.add_argument("--disable-features=TranslateUI")
            chrome_options.add_argument("--disable-background-networking")
            chrome_options.add_argument("--disable-sync")
            chrome_options.add_argument("--disable-default-apps")
            chrome_options.add_argument("--no-first-run")
            chrome_options.add_argument("--no-default-browser-check")

            # 日志和崩溃报告
            chrome_options.add_argument("--disable-logging")
            chrome_options.add_argument("--disable-gpu-logging")
            chrome_options.add_argument("--silent")
            chrome_options.add_argument("--log-level=3")  # 只显示严重错误

            # 是否无头模式（不显示浏览器界面）
            headless = os.getenv("BROWSER_HEADLESS", "False").lower() == "true"
            if headless:
                chrome_options.add_argument("--headless")

            # 使用webdriver-manager自动管理ChromeDriver版本
            try:
                service = Service(executable_path=settings.GOOGLE_DRIVER_PATH)
            except Exception as e:
                logger.warning(f"自动管理ChromeDriver失败，尝试使用本地驱动: {str(e)}")
                # 自动下载并管理与当前Chrome版本匹配的ChromeDriver
                driver_path = ChromeDriverManager().install()
                service = Service(executable_path=driver_path)
                logger.info(f"使用自动管理的ChromeDriver: {driver_path}")

            # 初始化Chrome浏览器
            self.driver = webdriver.Chrome(service=service, options=chrome_options)

            # 设置等待超时
            self.driver.implicitly_wait(10)
            logger.info("Chrome浏览器初始化成功")

        except Exception as e:
            logger.error(f"初始化Chrome浏览器失败: {str(e)}")
            raise

    def get_driver(self):
        """获取WebDriver实例"""
        if self.driver is None:
            self.init_browser()
        return self.driver

    def is_browser_alive(self) -> bool:
        """检查浏览器是否还活着"""
        if self.driver is None:
            return False
        try:
            # 尝试获取当前URL来检查浏览器是否还活着
            _ = self.driver.current_url
            return True
        except Exception:
            return False

    def close(self):
        """关闭浏览器"""
        if self.driver is not None:
            try:
                # 先关闭所有窗口
                for handle in self.driver.window_handles:
                    self.driver.switch_to.window(handle)
                    self.driver.close()
                # 然后退出驱动
                self.driver.quit()
            except Exception as e:
                logger.warning(f"关闭浏览器时出现警告: {str(e)}")
                try:
                    # 强制退出
                    self.driver.quit()
                except:
                    pass
            finally:
                self.driver = None
                logger.info("Chrome浏览器已关闭")


def _ensure_browser_initialized() -> webdriver.Chrome:
    """确保浏览器实例已初始化"""
    global _browser_manager

    # 检查浏览器实例是否需要重新初始化
    if _browser_manager is None or not _browser_manager.is_browser_alive():
        # 如果浏览器实例已关闭，重置全局变量
        if _browser_manager is not None:
            _browser_manager.close()

        _browser_manager = BrowserManager()

    return _browser_manager.get_driver()


# 工具装饰器，用于处理浏览器操作中的异常
def browser_tool_decorator(func):
    @wraps(func)
    async def wrapper(*args, **kwargs):
        try:
            # 确保浏览器实例存在
            _ensure_browser_initialized()
            # 调用原始函数
            result = await func(*args, **kwargs) if asyncio.iscoroutinefunction(func) else func(*args, **kwargs)
            return result
        except TimeoutException:
            logger.error(f"操作超时: {func.__name__}")
            return f"操作超时: {func.__name__}"
        except NoSuchElementException:
            logger.error(f"找不到元素: {func.__name__}")
            return f"找不到指定的元素: {func.__name__}"
        except ElementNotInteractableException:
            logger.error(f"元素不可交互: {func.__name__}")
            return f"元素不可交互: {func.__name__}"
        except WebDriverException as e:
            logger.error(f"WebDriver异常: {str(e)}")
            return f"浏览器操作异常: {str(e)}"
        except Exception as e:
            logger.error(f"未知异常: {str(e)}")
            return f"操作失败: {str(e)}"

    return wrapper


@tool
@browser_tool_decorator
async def browser_go_to_url(url: str) -> str:
    """
    导航到指定的URL地址。

    参数:
        url: 要导航到的URL地址

    返回:
        导航结果的描述
    """
    driver = _ensure_browser_initialized()
    driver.get(url)
    return f"已导航至 {url}"


@tool
@browser_tool_decorator
async def browser_go_back() -> str:
    """
    返回到上一页。

    返回:
        操作结果的描述
    """
    driver = _ensure_browser_initialized()
    driver.back()
    return "已返回上一页"


@tool
@browser_tool_decorator
async def browser_web_search(query: str) -> str:
    """
    执行网页搜索并导航到第一个结果。

    参数:
        query: 搜索查询内容

    返回:
        搜索和导航结果的描述
    """
    driver = _ensure_browser_initialized()

    # 执行网页搜索
    search_results = _web_search_tool.search(query, num_results=1)
    # 导航到第一个搜索结果
    if search_results:
        first_search_result = search_results[0]
        url_to_navigate = first_search_result.url

        driver.get(url_to_navigate)
        return f"搜索 '{query}' 并导航至: {url_to_navigate}"
    return f"搜索 '{query}' 未找到结果"


@tool
@browser_tool_decorator
async def browser_click_element(index: int) -> str:
    """
    点击指定索引的页面元素。

    参数:
        index: 要点击的元素索引

    返回:
        点击操作结果的描述
    """
    driver = _ensure_browser_initialized()

    try:
        # 尝试多种选择器方式来找到元素
        selectors = [
            f"[data-index='{index}']",
            f":nth-child({index})",
            f"*:nth-of-type({index})"
        ]

        element = None
        for selector in selectors:
            try:
                element = WebDriverWait(driver, 2).until(
                    EC.element_to_be_clickable((By.CSS_SELECTOR, selector))
                )
                break
            except:
                continue

        if element is None:
            # 如果按索引找不到，尝试找到所有可点击元素
            clickable_elements = driver.find_elements(By.CSS_SELECTOR,
                                                      "a, button, input[type='button'], input[type='submit'], [onclick], [role='button']")
            if 0 <= index < len(clickable_elements):
                element = clickable_elements[index]
            else:
                return f"找不到索引为 {index} 的可点击元素，当前页面共有 {len(clickable_elements)} 个可点击元素"

        element.click()
        return f"已点击索引为 {index} 的元素"
    except Exception as e:
        return f"点击元素失败: {str(e)}"


@tool
@browser_tool_decorator
async def browser_input_text(index: int, text: str) -> str:
    """
    在指定索引的输入框中输入文本。

    参数:
        index: 输入框元素的索引
        text: 要输入的文本内容

    返回:
        输入操作结果的描述
    """
    driver = _ensure_browser_initialized()

    try:
        # 查找所有输入元素
        input_elements = driver.find_elements(By.CSS_SELECTOR, "input, textarea")

        if 0 <= index < len(input_elements):
            element = input_elements[index]
            element.clear()
            element.send_keys(text)
            return f"已在索引为 {index} 的元素中输入 '{text}'"
        else:
            return f"找不到索引为 {index} 的输入元素，当前页面共有 {len(input_elements)} 个输入元素"
    except Exception as e:
        return f"输入文本失败: {str(e)}"


@tool
@browser_tool_decorator
async def browser_scroll_down(scroll_amount: Optional[int] = None) -> str:
    """
    向下滚动页面。

    参数:
        scroll_amount: 可选，滚动的像素数量，默认为浏览器窗口高度

    返回:
        滚动操作结果的描述
    """
    driver = _ensure_browser_initialized()
    amount = scroll_amount if scroll_amount is not None else 720  # 默认窗口高度
    driver.execute_script(f"window.scrollBy(0, {amount});")
    return f"已向下滚动 {amount} 像素"


@tool
@browser_tool_decorator
async def browser_scroll_up(scroll_amount: Optional[int] = None) -> str:
    """
    向上滚动页面。

    参数:
        scroll_amount: 可选，滚动的像素数量，默认为浏览器窗口高度

    返回:
        滚动操作结果的描述
    """
    driver = _ensure_browser_initialized()
    amount = scroll_amount if scroll_amount is not None else 720  # 默认窗口高度
    driver.execute_script(f"window.scrollBy(0, {-amount});")
    return f"已向上滚动 {amount} 像素"


@tool
@browser_tool_decorator
async def browser_scroll_to_text(text: str) -> str:
    """
    滚动到包含指定文本的元素位置。

    参数:
        text: 要滚动到的文本内容

    返回:
        滚动操作结果的描述
    """
    driver = _ensure_browser_initialized()

    try:
        # 使用XPath查找包含文本的元素
        element = driver.find_element(By.XPATH, f"//*[contains(text(), '{text}')]")
        driver.execute_script("arguments[0].scrollIntoView(true);", element)
        return f"已滚动到文本: '{text}'"
    except NoSuchElementException:
        return f"未找到包含文本 '{text}' 的元素"


@tool
@browser_tool_decorator
async def browser_send_keys(keys: str) -> str:
    """
    发送键盘按键。

    参数:
        keys: 要发送的键盘按键（如 'Enter', 'Tab', 'Escape' 等）

    返回:
        按键操作结果的描述
    """
    driver = _ensure_browser_initialized()

    # 按键映射
    key_mapping = {
        'Enter': Keys.RETURN,
        'Tab': Keys.TAB,
        'Escape': Keys.ESCAPE,
        'Space': Keys.SPACE,
        'Backspace': Keys.BACKSPACE,
        'Delete': Keys.DELETE,
        'ArrowUp': Keys.ARROW_UP,
        'ArrowDown': Keys.ARROW_DOWN,
        'ArrowLeft': Keys.ARROW_LEFT,
        'ArrowRight': Keys.ARROW_RIGHT,
    }

    key_to_send = key_mapping.get(keys, keys)

    # 获取当前活动元素并发送按键
    active_element = driver.switch_to.active_element
    active_element.send_keys(key_to_send)
    return f"已发送按键: {keys}"


@tool
@browser_tool_decorator
async def browser_extract_content(goal: str) -> str:
    """
    从当前页面提取指定目标的内容。

    参数:
        goal: 提取目标描述，说明需要从页面中提取什么信息

    返回:
        提取的内容（JSON格式）
    """
    driver = _ensure_browser_initialized()
    llm = LLMFactory.create_llm("deepseek-chat")

    # 获取最大内容长度配置
    max_content_length = getattr(config, "max_content_length", 2000)

    # 获取页面内容
    try:
        import markdownify
        content = markdownify.markdownify(driver.page_source)
    except ImportError:
        # 如果没有markdownify，使用纯文本
        content = driver.find_element(By.TAG_NAME, "body").text

    prompt = f"""\
您的任务是从网页内容中提取特定信息。请根据提取目标，从页面内容中提取相关信息，并以JSON格式返回。

提取目标: {goal}

请按以下JSON格式返回结果：
{{
  "extracted_content": {{
    "title": "页面或内容的标题",
    "text": "提取到的主要文本内容",
    "key_points": ["关键点1", "关键点2", "关键点3"],
    "links": ["相关链接1", "相关链接2"],
    "metadata": {{
      "source": "提取内容的来源描述",
      "extraction_target": "{goal}",
      "content_length": "原始内容长度"
    }}
  }}
}}

页面内容:
{content[:max_content_length]}
"""

    messages = [{"role": "user", "content": prompt}]

    # 直接调用LLM获取结构化响应
    response = await llm.ainvoke(messages)

    if response:
        # 尝试解析JSON响应
        try:
            # 清理响应文本，移除可能的markdown代码块标记
            response_text = response.strip()
            if response_text.startswith('```json'):
                response_text = response_text[7:]
            if response_text.endswith('```'):
                response_text = response_text[:-3]
            response_text = response_text.strip()

            # 解析JSON
            extracted_data = json.loads(response_text)
            return f"从页面提取的内容:\n{json.dumps(extracted_data, indent=2, ensure_ascii=False)}"
        except json.JSONDecodeError:
            # 如果JSON解析失败，返回原始响应
            return f"从页面提取的内容:\n{response}"

    return "未从页面提取到内容"


@tool
@browser_tool_decorator
async def browser_switch_tab(tab_id: int) -> str:
    """
    切换到指定ID的标签页。

    参数:
        tab_id: 要切换到的标签页ID

    返回:
        切换操作结果的描述
    """
    driver = _ensure_browser_initialized()

    try:
        window_handles = driver.window_handles
        if 0 <= tab_id < len(window_handles):
            driver.switch_to.window(window_handles[tab_id])
            return f"已切换到标签页 {tab_id}"
        else:
            return f"错误: 标签页 {tab_id} 不存在，当前有 {len(window_handles)} 个标签页"
    except Exception as e:
        return f"切换标签页失败: {str(e)}"


@tool
@browser_tool_decorator
async def browser_open_tab(url: str) -> str:
    """
    打开新的标签页并导航到指定URL。

    参数:
        url: 新标签页要导航到的URL地址

    返回:
        新标签页创建结果的描述
    """
    driver = _ensure_browser_initialized()

    try:
        # 打开新标签页
        driver.execute_script("window.open('');")
        # 切换到新标签页
        driver.switch_to.window(driver.window_handles[-1])
        # 导航到指定URL
        driver.get(url)
        return f"已打开新标签页: {url}"
    except Exception as e:
        return f"打开新标签页失败: {str(e)}"


@tool
@browser_tool_decorator
async def browser_close_tab() -> str:
    """
    关闭当前标签页。

    返回:
        关闭操作结果的描述
    """
    driver = _ensure_browser_initialized()

    try:
        window_handles = driver.window_handles
        if len(window_handles) > 1:
            driver.close()
            # 切换到剩余的标签页
            driver.switch_to.window(driver.window_handles[0])
            return "已关闭当前标签页"
        else:
            return "无法关闭最后一个标签页"
    except Exception as e:
        return f"关闭标签页失败: {str(e)}"


@tool
@browser_tool_decorator
async def browser_wait(seconds: int = 3) -> str:
    """
    等待指定的秒数。

    参数:
        seconds: 等待的秒数，默认为3秒

    返回:
        等待操作结果的描述
    """
    await asyncio.sleep(seconds)
    return f"已等待 {seconds} 秒"


@tool
@browser_tool_decorator
async def browser_execute_javascript(script: str) -> str:
    """
    执行JavaScript代码。

    参数:
        script: 要执行的JavaScript代码

    返回:
        执行结果的描述
    """
    driver = _ensure_browser_initialized()

    try:
        result = driver.execute_script(script)
        if result is None:
            result_str = "无返回值"
        else:
            try:
                # 尝试将结果转换为字符串
                result_str = str(result)
                if len(result_str) > 1000:
                    result_str = result_str[:1000] + "..."
            except:
                result_str = "无法显示结果"

        return f"成功执行JavaScript，结果: {result_str}"
    except Exception as e:
        return f"执行JavaScript失败: {str(e)}"


class BrowserUseToolManager(ToolManager):
    """浏览器使用工具管理器"""

    @property
    def category_name(self) -> str:
        return "浏览器自动化工具集"

    @property
    def category_code(self) -> str:
        return "browser_use"

    @property
    def category_description(self) -> str:
        return "提供完整的浏览器自动化功能工具集，包括网页导航、元素交互、内容提取、标签页管理等功能"

    def get_tool_configs(self) -> List[Dict[str, Any]]:
        # 工具1：导航到URL
        go_to_url_tool = SingleToolConfig(
            tool_id=1,
            tool_name="browser_go_to_url",
            description="导航到指定的URL地址",
            is_enabled=True
        )
        go_to_url_tool.add_parameter(
            param_name="url",
            param_type="string",
            description="要导航到的URL地址",
            is_required=True
        )

        # 工具2：返回上一页
        go_back_tool = SingleToolConfig(
            tool_id=2,
            tool_name="browser_go_back",
            description="返回到上一页",
            is_enabled=True
        )

        # 工具3：网页搜索
        web_search_tool = SingleToolConfig(
            tool_id=3,
            tool_name="browser_web_search",
            description="执行网页搜索并导航到第一个结果",
            is_enabled=True
        )
        web_search_tool.add_parameter(
            param_name="query",
            param_type="string",
            description="搜索查询内容",
            is_required=True
        )

        # 工具4：点击元素
        click_tool = SingleToolConfig(
            tool_id=4,
            tool_name="browser_click_element",
            description="点击指定索引的页面元素",
            is_enabled=True
        )
        click_tool.add_parameter(
            param_name="index",
            param_type="integer",
            description="要点击的元素索引",
            is_required=True
        )

        # 工具5：输入文本
        input_tool = SingleToolConfig(
            tool_id=5,
            tool_name="browser_input_text",
            description="在指定索引的输入框中输入文本",
            is_enabled=True
        )
        input_tool.add_parameter(
            param_name="index",
            param_type="integer",
            description="输入框元素的索引",
            is_required=True
        )
        input_tool.add_parameter(
            param_name="text",
            param_type="string",
            description="要输入的文本内容",
            is_required=True
        )

        # 工具6：向下滚动
        scroll_down_tool = SingleToolConfig(
            tool_id=6,
            tool_name="browser_scroll_down",
            description="向下滚动页面",
            is_enabled=True
        )
        scroll_down_tool.add_parameter(
            param_name="scroll_amount",
            param_type="integer",
            description="滚动的像素数量，默认为浏览器窗口高度",
            is_required=False
        )

        # 工具7：向上滚动
        scroll_up_tool = SingleToolConfig(
            tool_id=7,
            tool_name="browser_scroll_up",
            description="向上滚动页面",
            is_enabled=True
        )
        scroll_up_tool.add_parameter(
            param_name="scroll_amount",
            param_type="integer",
            description="滚动的像素数量，默认为浏览器窗口高度",
            is_required=False
        )

        # 工具8：滚动到文本
        scroll_to_text_tool = SingleToolConfig(
            tool_id=8,
            tool_name="browser_scroll_to_text",
            description="滚动到包含指定文本的元素位置",
            is_enabled=True
        )
        scroll_to_text_tool.add_parameter(
            param_name="text",
            param_type="string",
            description="要滚动到的文本内容",
            is_required=True
        )

        # 工具9：发送按键
        send_keys_tool = SingleToolConfig(
            tool_id=9,
            tool_name="browser_send_keys",
            description="发送键盘按键",
            is_enabled=True
        )
        send_keys_tool.add_parameter(
            param_name="keys",
            param_type="string",
            description="要发送的键盘按键（如 'Enter', 'Tab', 'Escape' 等）",
            is_required=True
        )

        # 工具10：提取内容
        extract_tool = SingleToolConfig(
            tool_id=10,
            tool_name="browser_extract_content",
            description="从当前页面提取指定目标的内容",
            is_enabled=True
        )
        extract_tool.add_parameter(
            param_name="goal",
            param_type="string",
            description="提取目标描述，说明需要从页面中提取什么信息",
            is_required=True
        )

        # 工具11：切换标签页
        switch_tab_tool = SingleToolConfig(
            tool_id=11,
            tool_name="browser_switch_tab",
            description="切换到指定ID的标签页",
            is_enabled=True
        )
        switch_tab_tool.add_parameter(
            param_name="tab_id",
            param_type="integer",
            description="要切换到的标签页ID",
            is_required=True
        )

        # 工具12：打开新标签页
        open_tab_tool = SingleToolConfig(
            tool_id=12,
            tool_name="browser_open_tab",
            description="打开新的标签页并导航到指定URL",
            is_enabled=True
        )
        open_tab_tool.add_parameter(
            param_name="url",
            param_type="string",
            description="新标签页要导航到的URL地址",
            is_required=True
        )

        # 工具13：关闭标签页
        close_tab_tool = SingleToolConfig(
            tool_id=13,
            tool_name="browser_close_tab",
            description="关闭当前标签页",
            is_enabled=True
        )

        # 工具14：等待
        wait_tool = SingleToolConfig(
            tool_id=14,
            tool_name="browser_wait",
            description="等待指定的秒数",
            is_enabled=True
        )
        wait_tool.add_parameter(
            param_name="seconds",
            param_type="integer",
            description="等待的秒数，默认为3秒",
            default_value=3,
            is_required=False
        )

        # 工具15：执行JavaScript
        execute_js_tool = SingleToolConfig(
            tool_id=15,
            tool_name="browser_execute_javascript",
            description="执行JavaScript代码",
            is_enabled=True
        )
        execute_js_tool.add_parameter(
            param_name="script",
            param_type="string",
            description="要执行的JavaScript代码",
            is_required=True
        )

        return [
            go_to_url_tool.to_dict(),
            go_back_tool.to_dict(),
            web_search_tool.to_dict(),
            click_tool.to_dict(),
            input_tool.to_dict(),
            scroll_down_tool.to_dict(),
            scroll_up_tool.to_dict(),
            scroll_to_text_tool.to_dict(),
            send_keys_tool.to_dict(),
            extract_tool.to_dict(),
            switch_tab_tool.to_dict(),
            open_tab_tool.to_dict(),
            close_tab_tool.to_dict(),
            wait_tool.to_dict(),
            execute_js_tool.to_dict()
        ]

    def get_langchain_tool(self):
        """返回工具集中的所有工具"""
        return [
            browser_go_to_url,
            browser_go_back,
            browser_web_search,
            browser_click_element,
            browser_input_text,
            browser_scroll_down,
            browser_scroll_up,
            browser_scroll_to_text,
            browser_send_keys,
            browser_extract_content,
            browser_switch_tab,
            browser_open_tab,
            browser_close_tab,
            browser_wait,
            browser_execute_javascript
        ]


# 创建全局实例
browser_use_tool_manager = BrowserUseToolManager()
